use std::collections::BTreeMap;

// https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/PropertyLists/OldStylePlists/OldStylePLists.html
use super::error::Error;
use super::reader::Reader;

#[derive(Debug, Clone, PartialEq)]
pub enum Event<'a> {
	Error(Error, (usize, usize)),

	Array,
	Dictionary,
	End,

	Data(&'a str),
	String(&'a str),
	Integer(i128),
	Real(f64),
}

pub struct Parser<'a> {
	r: Reader<'a>,
}
impl<'a> Parser<'a> {
	pub fn new(content: &'a str) -> Self {
		Self { r: Reader::new(content) }
	}
	pub fn pos(&self) -> (usize, usize) {
		self.r.pos()
	}
	pub fn peak(&self) -> Self {
		Self {
			r: Reader::new(self.r.tail()),
		}
	}
	pub fn read_next(&mut self) -> Result<Event<'a>, Error> {
		loop {
			self.r.eat_any(" \t\n\r");
			match self.r.peek().ok_or(Error::Eof)? {
				'\'' => return self.read_quoted('\''),
				'"' => return self.read_quoted('"'),
				'/' => match self.r.peek_many().nth(1).ok_or(Error::Msg("incomplete comment"))? {
					'/' => self.read_line_comment()?,
					'*' => self.read_block_comment()?,
					_ => return self.read_unquoted(),
				},
				'<' => return self.read_data(),
				'{' => {
					self.r.next();
					return Ok(Event::Dictionary);
				}
				'(' => {
					self.r.next();
					return Ok(Event::Array);
				}
				'}' | ')' => {
					self.r.next();
					return Ok(Event::End);
				}
				';' | ',' | '=' => {
					self.r.next();
				}
				_ => return self.read_unquoted(),
			}
		}
	}
	pub fn expect(&mut self, expect: Event<'_>) -> Result<(), Error> {
		let e = self.read_next()?;
		if e != expect {
			return Err(Error::Expect(format!("{:?}", expect), Some(format!("{:?}", e))));
		}
		Ok(())
	}
	pub fn expect_string(&mut self) -> Result<&'a str, Error> {
		match self.read_next()? {
			Event::String(s) => Ok(s),
			e => Err(Error::Expect("Event::Ident".to_string(), Some(format!("{:?}", e)))),
		}
	}
	pub fn parse<T: FromPlist>(&mut self) -> Result<T, Error> {
		T::from_plist(self)
	}
	pub fn read_data(&mut self) -> Result<Event<'a>, Error> {
		self.r.eat_char('<').then_some("").ok_or(Error::Msg("expect <"))?;
		let event = self.r.capture(|r| r.eat_until_any(">")).map(Event::Data).unwrap_or(Event::Data(""));
		self.r.eat_char('<').then_some("").ok_or(Error::Msg("expect >"))?;
		Ok(event)
	}
	pub fn read_line_comment(&mut self) -> Result<(), Error> {
		let eat = self.r.eat_str("//")
			&& {
				self.r.eat_until_any("\n\r");
				true
			} && self.r.eat_any("\n\r");
		eat.then_some("").ok_or(Error::Msg("incomplete comment"))?;
		Ok(())
	}
	pub fn read_block_comment(&mut self) -> Result<(), Error> {
		let eat = self.r.eat_str("/*")
			&& {
				while !self.r.starts_with("*/") {
					self.r.next();
				}
				true
			} && self.r.eat_str("*/");
		eat.then_some("").ok_or(Error::Msg("incomplete comment"))?;
		Ok(())
	}
	pub fn read_quoted(&mut self, quote: char) -> Result<Event<'a>, Error> {
		self.r.eat_char(quote).then_some("").ok_or(Error::Msg("expect quote"))?;
		let event = self.r.capture(|r| r.eat_until_char(quote)).unwrap_or_default();
		self.r.eat_char(quote).then_some("").ok_or(Error::Msg("expect quote"))?;
		Ok(Event::String(event))
	}
	pub fn read_unquoted(&mut self) -> Result<Event<'a>, Error> {
		let v = self
			.r
			.capture(|r| r.eat_until_any(" \t\n\r=;,)}>"))
			.ok_or(Error::Msg("expect unquoted"))?;
		Ok(Event::String(v))
	}
}
impl<'a> Iterator for Parser<'a> {
	type Item = Event<'a>;
	fn next(&mut self) -> Option<Self::Item> {
		match self.read_next() {
			Ok(event) => Some(event),
			Err(error) => match error {
				Error::Eof => None,
				error => Some(Event::Error(error, self.r.pos())),
			},
		}
	}
}

pub trait FromPlist: Sized {
	fn from_plist(r: &mut Parser<'_>) -> Result<Self, Error>;
}
impl<T: FromPlist> FromPlist for Option<T> {
	fn from_plist(r: &mut Parser<'_>) -> Result<Self, Error> {
		Ok(Some(T::from_plist(r)?))
	}
}
impl FromPlist for bool {
	fn from_plist(r: &mut Parser<'_>) -> Result<Self, Error> {
		match r.read_next()? {
			Event::String(v) => match v.parse::<i128>() {
				Ok(v) => Ok(v == 1),
				Err(e) => return Err(Error::Integer(e)),
			},
			e => return Err(Error::unexpect(e)),
		}
	}
}
impl FromPlist for u64 {
	fn from_plist(r: &mut Parser<'_>) -> Result<Self, Error> {
		match r.read_next()? {
			Event::String(v) => match v.parse() {
				Ok(v) => Ok(v),
				Err(e) => return Err(Error::Integer(e)),
			},
			e => return Err(Error::unexpect(e)),
		}
	}
}
impl FromPlist for i64 {
	fn from_plist(r: &mut Parser<'_>) -> Result<Self, Error> {
		match r.read_next()? {
			Event::String(v) => match v.parse() {
				Ok(v) => Ok(v),
				Err(e) => return Err(Error::Integer(e)),
			},
			e => return Err(Error::unexpect(e)),
		}
	}
}
impl FromPlist for i128 {
	fn from_plist(r: &mut Parser<'_>) -> Result<Self, Error> {
		match r.read_next()? {
			Event::String(v) => match v.parse() {
				Ok(v) => Ok(v),
				Err(e) => return Err(Error::Integer(e)),
			},
			e => return Err(Error::unexpect(e)),
		}
	}
}
impl FromPlist for f64 {
	fn from_plist(r: &mut Parser<'_>) -> Result<Self, Error> {
		match r.read_next()? {
			Event::String(v) => match v.parse() {
				Ok(v) => Ok(v),
				Err(e) => return Err(Error::Real(e)),
			},
			e => return Err(Error::unexpect(e)),
		}
	}
}
impl FromPlist for String {
	fn from_plist(r: &mut Parser<'_>) -> Result<Self, Error> {
		match r.read_next()? {
			Event::String(v) => Ok(v.into()),
			e => return Err(Error::unexpect(e)),
		}
	}
}
impl<T: FromPlist> FromPlist for Vec<T> {
	fn from_plist(r: &mut Parser<'_>) -> Result<Self, Error> {
		r.expect(Event::Array)?;
		let mut this = Self::default();
		loop {
			if r.peak().read_next()? == Event::End {
				_ = r.next();
				break;
			}
			this.push(T::from_plist(r)?);
		}
		Ok(this)
	}
}
impl<T: FromPlist> FromPlist for BTreeMap<String, T> {
	fn from_plist(r: &mut Parser<'_>) -> Result<Self, Error> {
		r.expect(Event::Dictionary)?;
		let mut this = Self::default();
		loop {
			if r.peak().read_next()? == Event::End {
				_ = r.next();
				break;
			}
			this.insert(String::from_plist(r)?, T::from_plist(r)?);
		}
		Ok(this)
	}
}
#[derive(Debug, Clone)]
pub enum Value {
	Dict(BTreeMap<String, Value>),
	Array(Vec<Value>),
	String(String),
	Data(String),
}
impl FromPlist for Value {
	fn from_plist(r: &mut Parser<'_>) -> Result<Self, Error> {
		match r.peak().read_next()? {
			Event::Error(error, _) => return Err(error),
			Event::Array => Ok(Self::Array(Vec::from_plist(r)?)),
			Event::Dictionary => Ok(Self::Dict(BTreeMap::from_plist(r)?)),
			Event::Data(v) => {
				r.read_next()?;
				Ok(Self::Data(v.into()))
			}
			Event::String(v) => {
				r.read_next()?;
				Ok(Self::String(v.into()))
			}
			e => {
				r.read_next()?;
				return Err(Error::unexpect(e));
			}
		}
	}
}

#[cfg(test)]
mod tests {
	use super::*;
	#[test]
	fn read_plist_ascii_sample() {
		let data = std::fs::read_to_string("assets/plist/ascii-sample.plist").unwrap();
		let p = Parser::new(&data);
		for s in p {
			match s {
				Event::Error(e, p) => panic!("{:?} {:?}", p, e),
				_ => {}
			}
		}
	}
	#[test]
	fn read_plist_ascii_animals() {
		let data = std::fs::read_to_string("assets/plist/ascii-animals.plist").unwrap();
		let p = Parser::new(&data);
		for s in p {
			match s {
				Event::Error(e, p) => panic!("{:?} {:?}", p, e),
				_ => {}
			}
		}
	}
}
